#pragma once
#include <map>
#include <queue>
#include <chrono>
#include <iostream>
#include <functional>
#include <cstring>

class RUDP
{
private:
	//	protocol header length
	static const unsigned char PSH = 0x10;
	static const unsigned char ACK = 0x11;

	//	protocol header type
	static const unsigned short HeaderLen = 16;

public:
	//	RUDP Fragment type
	struct UserData
	{
		UserData(char* buf, unsigned int size) :
			buf(buf),
			size(size) {}

		char *buf;
		unsigned int size;
	};

	struct Fragment : public UserData
	{
		Fragment(char* buf, unsigned int size) : 
			UserData(buf, size),
			TS(0),
			tri_fastrsnd(false) {}

		Fragment(char* buf, unsigned int size, unsigned int TS) :
			UserData(buf, size),
			TS(TS),
			tri_fastrsnd(false) {}

		unsigned int TS;
		bool tri_fastrsnd;
	};

public:
	RUDP(unsigned int initRTO, unsigned short fast_rsend, unsigned short MTU, unsigned char conv);
	~RUDP();
	void SetOutput(std::function<void(const char *buf, int size)> send_func);
	void Input(char *msg, unsigned int size, const char *ip);
	bool Send(const char *msg, unsigned short size);
	bool Recv(std::queue<UserData *> &msg);
	unsigned int PendingSendCount();
	unsigned int PendingRecvCount();
	void Disable();
	bool isEnable();
	virtual void Update();

	static void releaseFragment(Fragment *msg);

private:
	char *PSHHandler(char *msg, char *end_msg);
	char *ACKHandler(char *msg);
	void PushFragment(unsigned int seq, char *data, unsigned int size);
	void ParseACKFragment(unsigned int seq, unsigned int ts);
	void UpdateFragmentTS(char *msg);
	void CheckRecvQueue();
	void UpdateRecvRound();
	std::map<unsigned int, RUDP::Fragment *>::iterator DelSendBuf(unsigned int ack, bool &succes);
	Fragment *NewFragment(const char *src, unsigned int size);
	unsigned int Clock();
	bool InRange(unsigned int v, unsigned int min, unsigned int max);

private:
	bool enable;
	unsigned char conv;
	unsigned int RTO;
	unsigned short MTU;
	unsigned int fast_rsend;
	unsigned int send_nxt;
	unsigned int recv_nxt;
	unsigned int max_seq;
	bool seq_rounded;
	std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp;
	char *buffer;
	std::map<unsigned int, Fragment *> recv_queue;
	std::queue<Fragment *> recv_buf;
	std::map<unsigned int, Fragment *> send_buf;
	std::queue<Fragment *> ack_queue;
	std::function<void(const char *buf, int size)> send_func;
};

void Test();